import os
import shutil
import time
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
import platform
import json
import random
from datetime import datetime
import threading
from settings import g_settings,g_logger,PROJECT_NAME,PROJECT_VERSION,PROJECT_START_TIMESTAMP
from utils.Utils import xc_gen_random_code

class HttpController(tornado.web.RequestHandler):

    def initialize(self):
        # 初始化函数，一般用于初始化环境变量
        pass

    def get_request_ip(self):
        request_ip = self.request.headers.get("X-Real-IP") or self.request.headers.get("X-Forwarded-For") or \
                    self.request.remote_ip
        return request_ip

    def get_request_port(self):
        # address = self.request.connection.context.address
        request_port = 20000
        return request_port

    def request_get_params(self):
        params = {}
        for key in self.request.arguments:
            params[key] = self.get_argument(key)
        return params

    def request_post_params(self):
        post_params = self.request.arguments
        post_params = {x: post_params.get(x)[0].decode("utf-8") for x in post_params.keys()}

        # print("获取POST 表单请求参数：",type(post_params),post_params)

        if not post_params:
            post_params = self.request.body.decode('utf-8')
            post_params = json.loads(post_params)
            # print("获取POST json请求参数：", type(post_params), post_params)

        return post_params

class IndexController(HttpController):
    async def get(self, *args, **kwargs):
        data = await self.do()
        self.write(data)

    async def do(self):
        request_params = self.request_get_params()

        res = {
            "project_name": PROJECT_NAME,
            "project_version": PROJECT_VERSION,
            "project_start_timestamp": PROJECT_START_TIMESTAMP,
            "settings": g_settings.getStr()
        }

        return json.dumps(res)
        # return json.dumps(data, cls=ComplexEncoder)

class XcnvsFsRecordUploadController(HttpController):
    async def post(self, *args, **kwargs):
        data = await self.do()
        self.write(data)

    async def do(self):
        ret = False
        msg = "unknown error"
        info = {}

        request_params = self.request_post_params()
        safe = request_params.get('safe',"").strip()

        if safe == g_settings.appKey + g_settings.appSecret:

            try:
                xcms_name = request_params.get('xcms_name', "").strip()  # xcms
                record_code = request_params.get('record_code', "").strip()  # sr20241119144521152

                files = self.request.files
                files_key_list = list(files.keys())

                if len(files_key_list) == 1:
                    __file_key = files_key_list[0]
                    __file = files[__file_key][0]

                    __filename: str = __file['filename']
                    if not __filename.endswith(".mp4"):
                        raise Exception("unsupported filename format")

                    __file_data = __file['body']
                    __content_type = __file['content_type']
                    __size = int(self.request.headers.get('Content-Length'))

                    # print(__content_type,__size)
                    upload_filename = "%d_%s.mp4" % (int(time.time()),xc_gen_random_code())
                    filedir_abs = "{fsStorageDir}/{xcms_name}/{record_code}".format(
                        fsStorageDir=g_settings.fsStorageDir,
                        xcms_name=xcms_name,
                        record_code=record_code
                    )
                    if not os.path.exists(filedir_abs):
                        os.makedirs(filedir_abs)
                    filepath_abs = filedir_abs+"/"+upload_filename

                    # 文件写入本地
                    f = open(filepath_abs,'wb')
                    f.write(__file_data)
                    f.close()

                    ret = True
                    msg = "success"

                    info["upload_filename"] = upload_filename # 新名称
                else:
                    msg = "the file are illegal"
            except Exception as e:
                msg = str(e)
        else:
            msg = "unsafe"

        res = {
            "code": 1000 if ret else 0,
            "msg": msg,
            "info": info
        }
        return json.dumps(res)

class XcnvsFsRecordAccessController(HttpController):
    async def get(self, *args, **kwargs):
        data = await self.do()
        self.write(data)

    async def do(self):
        msg = "unknown error"
        request_params = self.request_get_params()
        safe = request_params.get('safe',"").strip()

        if safe == g_settings.appKey + g_settings.appSecret:
            try:
                xcms_name = request_params.get('xcms_name', "").strip() # xcms
                record_code = request_params.get('record_code', "").strip() # sr20241119144521152
                filename = request_params.get("filename") # 48_1731998880_60.mp4

                if filename.endswith(".mp4"):
                    filepath = "{fsStorageDir}/{xcms_name}/{record_code}/{filename}".format(
                        fsStorageDir=g_settings.fsStorageDir,
                        xcms_name=xcms_name,
                        record_code=record_code,
                        filename=filename
                    )
                    if os.path.exists(filepath):
                        f = open(filepath,'rb')
                        f_body = f.read()
                        f_body_len = len(f_body)
                        f.close()

                        self.set_header('Content-Type', 'video/mp4')
                        self.set_header('Accept-Ranges', 'bytes')
                        self.set_header('Access-Control-Allow-Origin', "*")
                        self.set_header('Access-Control-Allow-Headers', "*")
                        self.set_header('Access-Control-Allow-Methods', "POST, GET, OPTIONS, DELETE")
                        self.set_header('Content-Length', f_body_len)

                        return f_body
                    else:
                        msg = "the playback address does not exist"
                else:
                    msg = "unsupported playback format"
            except Exception as e:
                msg = str(e)
        else:
            msg = "unsafe"

        return msg

class XcnvsFsVODUploadController(HttpController):
    async def post(self, *args, **kwargs):
        data = await self.do()
        self.write(data)

    async def do(self):
        ret = False
        msg = "unknown error"
        info = {}

        request_params = self.request_post_params()
        safe = request_params.get('safe',"").strip()

        if safe == g_settings.appKey + g_settings.appSecret:
            try:
                files = self.request.files
                files_key_list = list(files.keys())

                if len(files_key_list) == 1:
                    __file_key = files_key_list[0]
                    __file = files[__file_key][0]
                    __filename = __file['filename']
                    if not __filename.endswith(".mp4"):
                        raise Exception("unsupported filename format")

                    __file_data = __file['body']
                    __content_type = __file['content_type']
                    __size = int(self.request.headers.get('Content-Length'))

                    # print(__filename,__content_type,__size)

                    upload_filename = "%d_%s"%(int(time.time()),__filename)

                    filedir = datetime.now().strftime("%Y%m%d")
                    filedir_abs = "{mediaStorageDir}/{filedir}".format(
                        mediaStorageDir=g_settings.mediaStorageDir,
                        filedir=filedir
                    )
                    if not os.path.exists(filedir_abs):
                        os.makedirs(filedir_abs)

                    filepath_abs = filedir_abs+"/"+upload_filename
                    filepath = filedir+"/"+upload_filename

                    # print(filedir_abs)
                    # print(filepath)

                    # 文件写入本地
                    f = open(filepath_abs,'wb')
                    f.write(__file_data)
                    f.close()

                    ret = True
                    msg = "success"

                    info["upload_filepath"] = filepath
                    info["upload_filename"] = upload_filename

                else:
                    msg = "the file are illegal"

            except Exception as e:
                msg = str(e)
        else:
            msg = "unsafe"

        res = {
            "code": 1000 if ret else 0,
            "msg": msg,
            "info": info
        }
        return json.dumps(res)

class XcnvsFsVODListController(HttpController):

    async def get(self, *args, **kwargs):
        data = await self.do()
        self.write(data)

    async def do(self):
        ret = False
        msg = "unknown error"
        data = []

        request_params = self.request_get_params()
        safe = request_params.get('safe',"").strip()

        if safe == g_settings.appKey + g_settings.appSecret:
            try:
                __dirs = os.listdir(g_settings.mediaStorageDir)
                for __dir in __dirs:
                    __dir_path_abs = os.path.join(g_settings.mediaStorageDir,__dir)
                    __files = os.listdir(__dir_path_abs)
                    for __file in __files:
                        if __file.endswith(".mp4"):
                            __filepath_abs = os.path.join(__dir_path_abs,__file)
                            __filepath = "%s/%s"%(__dir,__file)
                            if os.path.exists(__filepath_abs):
                                data.append({
                                    "filepath":__filepath
                                })

                ret = True
                msg = "success"
            except Exception as e:
                msg = str(e)
        else:
            msg = "unsafe"

        res = {
            "code": 1000 if ret else 0,
            "msg": msg,
            "data": data
        }
        return json.dumps(res)
class XcnvsFsVODRemoveController(HttpController):

    async def post(self, *args, **kwargs):
        data = await self.do()
        self.write(data)

    async def do(self):
        ret = False
        msg = "unknown error"

        request_params = self.request_post_params()
        safe = request_params.get('safe',"").strip()

        if safe == g_settings.appKey + g_settings.appSecret:
            try:
                filepath = request_params.get("filepath","").strip()
                if filepath == "":
                    raise Exception("please fill in the filepath")
                if not filepath.endswith(".mp4"):
                    raise Exception("unsupported filepath format")

                filepath_abs = os.path.join(g_settings.mediaStorageDir,filepath)
                if os.path.exists(filepath_abs):
                    os.remove(filepath_abs)
                    ret = True
                    msg = "success"
                else:
                    msg = "filepath does not exist"

            except Exception as e:
                msg = str(e)
        else:
            msg = "unsafe"

        res = {
            "code": 1000 if ret else 0,
            "msg": msg
        }
        return json.dumps(res)

def xcnvs_fs(ip, port, debug=True):
    g_logger.info("xcnvs_fs() http://%s:%d" % (ip, port))
    g_logger.info(g_settings.getStr())

    def __cleanupThread():
        while True:
            xcms_dirs = os.listdir(g_settings.fsStorageDir)
            for __xcms_dir in xcms_dirs:
                __xcms_dir_abs = os.path.join(g_settings.fsStorageDir,__xcms_dir)
                recordCode_dirs = os.listdir(__xcms_dir_abs)
                for __recordCode_dir in recordCode_dirs:
                    __recordCode_dir_abs = os.path.join(__xcms_dir_abs,__recordCode_dir)
                    filenames = os.listdir(__recordCode_dir_abs)

                    __cur_timestamp = int(time.time())
                    for __filename in filenames:
                        if __filename.endswith(".mp4"):
                            __filename_arr = __filename.split("_")
                            if len(__filename_arr) == 2:
                                __filename_timestamp = int(__filename_arr[0])
                                if __cur_timestamp - __filename_timestamp > g_settings.fsStorageRecordDuration:
                                    # 文件已经超时，执行删除
                                    __filepath = os.path.join(__recordCode_dir_abs,__filename)
                                    try:
                                        os.remove(__filepath)
                                    except Exception as e:
                                        g_logger.error("failed to delete video filepath:%s,e=%s"%(__filepath,str(e)))

            time.sleep(g_settings.fsStorageCheckInterval)

    t = threading.Thread(target=__cleanupThread)
    t.daemon = True
    t.start()

    if "Windows" == platform.system() or "windows" == platform.system():
        reuse_port = False  # Windows平台不支持端口复用
        autoreload = False
        num_processes = 1
    else:
        reuse_port = True
        autoreload = False
        num_processes = g_settings.fsProcessNum

    app = tornado.web.Application([
            (r"/", IndexController),
            (r"/xcnvs/fs/record/upload", XcnvsFsRecordUploadController),
            (r"/xcnvs/fs/record/access", XcnvsFsRecordAccessController),
            (r"/xcnvs/fs/vod/upload", XcnvsFsVODUploadController),
            (r"/xcnvs/fs/vod/remove", XcnvsFsVODRemoveController),
            (r"/xcnvs/fs/vod/list", XcnvsFsVODListController),
        ],
        websocket_ping_interval=5, # WebSocket ping探活包发送间隔秒数
        xsrf_cookies=False,
        cookie_secret=PROJECT_NAME,
        autoreload=autoreload, #
        debug=debug
        )

    server = tornado.httpserver.HTTPServer(app, max_buffer_size=g_settings.fsMaxBufferSize)
    server.bind(port=port, reuse_port=reuse_port)
    server.start(num_processes=num_processes)
    tornado.ioloop.IOLoop.current().start()

if __name__ == '__main__':
    xcnvs_fs(g_settings.ip, g_settings.fsPort, debug=g_settings.debug)
